<?
/*
	Usage:
		
		Profiler::init();
		
		Profiler::start("db_connect");
			mysql_connect();
		Profiler::stop("db_connect");
		
		Profiler::start("a");
			a();
		Profiler::next("b");
			b();
		Profiler::next("c");
			c();
		Profiler::stop();
		
		Profiler::finish();
	
*/
class Profiler
{
	private static $points = Array();
	private static $buffer = Array();
	private static $sid;
	private static $storage;
	private static $bufferize;
	private static $fp;
	
	// static constructor
	// storage: 
	//						file   - writes in tmp/profiler/{$sid}.txt
	//						db     - writes in table "profiler" using current connection
	//						no     - use only internal buffer
	// bufferize: 
	//						false  - direct write to storage each result
	//						true   - do not store until ::flush() executed
	static function init($storage='file', $bufferize=true)
	{
		self::$sid 					= self::generateSid();
		self::$storage 			= $storage;
		self::$bufferize		= $bufferize;
		
		if(self::$storage=='file') 
		{
			self::$fp = fopen(BASE_DIR."/tmp/profiler/".self::$sid.".txt", "w");
			if( !self::$fp ) throw new Exception("Profiler: Can't open file for writing.");
		}
	}
	
	
	// start timing label
	static function start($label)
	{
		if( !isset(self::$sid) ) self::init();
		
		// для правильного порядка вложенных меток - помещаем вышестоящую в буфер перед вложенными
		if(sizeof(self::$points)>0)
		{
			$last = self::$points[sizeof(self::$points)-1];
			self::store($last["label"], 0, 0);
		}
		
		self::$points[]	= Array("dt"=>microtime(true), "level"=>sizeof(self::$points), "label"=>$label );
	}
	
	
	// next - stops current timing and starts new
	// NB: поддерживает вложенность функции, в случае смены уровня вложенности - останов не происходит
	static function next($label)
	{
		// останавливаем прошлый счетчик, если он есть
		$ret = self::stop();
		self::start($label);
		return $ret;
	}
	
	
	// stops current label
	// параметр не используется, оставлен для индикации в коде,
	// при необходимости показать для себя что именно останавливаем
	static function stop($label='')
	{
		if(sizeof(self::$points)==0) return;
		
		$last = array_pop(self::$points);
		$period = microtime(true) - $last['dt'];
		self::store($last['label'], $period, $last['level'] );
		
		return $period;
	}
	
	
	// сохраняем значение полученное при остановке счетчика (вызове stop)
	static function store($label, $period, $level)
	{
		$result = Array("label"  			=> $label,
										"period" 			=> $period,
										"level"				=> $level,
										"times"  			=> 1
										);
		$pos = sizeof(self::$buffer);
		foreach(self::$buffer as $k=>$v)
		{
			if($v['label']==$label)
			{
				$pos = $k;
				if($v["period"]>0)
				{
					$result["period"] += $v["period"];
					$result["times"]  += $v["times"];
				}
				break;
			}
		}
		self::$buffer[$pos] = $result;
		
		if(self::$storage=='no' || self::$bufferize) return;
		
		if(self::$storage=="file") 	self::storeFile( Array($result) );
		if(self::$storage=="db") 		self::storeDb  ( Array($result) );
	}
	
	
	// производим запись данных если была включена буферизация
	// закрываем используемые ресурсы
	static function finish()
	{
		while( sizeof(self::$points)>0 ) self::stop();
		
		if(self::$bufferize)
		{
			if(self::$storage=='file')	self::storeFile( self::$buffer );
			if(self::$storage=='db')		self::storeDB  ( self::$buffer );
		}
		
		if(self::$storage=='file')
		{
			fclose(self::$fp);
		}
	}
	
	
	
	
	
	
	
	
	
	// функция сохранения резульатов в файле
	private static function storeFile($results)
	{
		foreach($results as $res)
		{
			$prefix = '';
			if($res['level'] > 0)
			{
				$prefix = str_repeat('-', $res['level']).' ';
			}
			
			$label  = str_pad($res['label'], 25 - strlen($prefix));
			$period = str_pad(sprintf("%.7f", $res['period'] ), 10);
			$times  = ($res["times"]>1 ? '('.$res["times"].')' : '');
			
			fwrite( self::$fp, $prefix.$label.$period.$times."\r\n" );
		}
	}
	
	
	// функция сохранения резульатов в БД
	private static function storeDb($results)
	{
		// not realized yet
	}
	
	
	
	
	
	
	private static function generateSid()
	{
		$t1 = microtime(true);
		$sid = time();
		// do some sense-less operations to generate random number later
		preg_match("/^(http:\/\/)?([^\/]+)/i","http://www.php.net/index.html", $matches);
		$sid = $sid.(microtime(true)-$t1);
		$sid = str_replace('.','',$sid);
		$sid = str_replace('E-','',$sid);
		return $sid;
	}
}